using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using RideMe.Core.Properties;

namespace RideMe.Core
{
    public static class FileSystemService
    {
        public static event EventHandler<ProjectFileEventArgs> FileFound;

        /// <summary>
        /// Raises the FileFound event.
        /// </summary>
        /// <param name="e"></param>
        private static void OnFileFound(ProjectFileEventArgs e)
        {
            if (FileFound != null)
            {
                FileFound(null, e);
            }
        }

        /// <summary>
        /// Returns the contents of a file.
        /// </summary>
        /// <param name="path">The path to the file.</param>
        /// <returns></returns>
        public static string GetDocumentText(string path)
        {
            CheckFile(path);

            return File.ReadAllText(path);
        }

        public static int GetNumberOfSubFolders(string path)
        {
            return Directory.GetDirectories(path).Length;
        }

        public static int GetNumberOfFiles(string path)
        {
            return Directory.GetFiles(path).Length;
        }

        /// <summary>
        /// Appends the document.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="contents">The contents.</param>
        public static void AppendDocument(string path, string contents)
        {
            try
            {
                File.AppendAllText(path, contents);
            }
            catch (ArgumentException)
            {
            }
        }

        /// <summary>
        /// Saves the document.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="contents">The contents.</param>
        public static void SaveDocument(string path, string contents)
        {
            try
            {
                File.WriteAllText(path, contents);
            }
            catch (ArgumentException)
            {
            }
        }

        /// <summary>
        /// Creates the directory.
        /// </summary>
        /// <param name="path">The path.</param>
        public static void CreateDirectory(string path)
        {
            Directory.CreateDirectory(path);
        }

        /// <summary>
        /// Copies the directory.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="destination">The destination.</param>
        public static void CopyDirectory(string source, string destination)
        {
            string[] files;

            // make sure the source directory exists
            if (!Directory.Exists(source))
            {
                // if not, throw an exception
                throw new DirectoryNotFoundException(source);
            }

            if (!Directory.Exists(destination))
            {
                Directory.CreateDirectory(destination);
            }

            files = Directory.GetFileSystemEntries(source);
            foreach (string Element in files)
            {
                // Sub directories
                if (Directory.Exists(Element))
                {
                    CopyDirectory(Element, Path.Combine(destination, Path.GetFileName(Element)));
                }
                // Files in directory
                else
                {
                    File.Copy(Element, Path.Combine(destination, Path.GetFileName(Element)), true);
                }
            }
        }

        /// <summary>
        /// Deletes the document.
        /// </summary>
        /// <param name="path">The path.</param>
        public static void DeleteDocument(string path)
        {
            CheckFile(path);

            File.Delete(path);
        }

        /// <summary>
        /// Deletes the directory.
        /// </summary>
        /// <param name="path">The path.</param>
        public static void DeleteDirectory(string path)
        {
            CheckDirectory(path);

            Directory.Delete(path, true);
        }

        /// <summary>
        /// Checks the directory for validity, and throws
        /// an IOException if the directory isn't valid.
        /// </summary>
        /// <param name="path">The path.</param>
        private static void CheckDirectory(string path)
        {
            if (!Validator.ValidDirectory(path))
            {
                throw new IOException(Resources.DirectoryDoesNotExist);
            }
        }

        /// <summary>
        /// Checks the file for validity, and throws
        /// an IOException if the file isn't valid.
        /// </summary>
        /// <param name="path">The path.</param>
        private static void CheckFile(string path)
        {
            if (!Validator.ValidFile(path))
            {
                throw new IOException(Resources.FileDoesNotExist);
            }
        }

        /// <summary>
        /// Renames the file.
        /// </summary>
        /// <param name="oldPath">The old path.</param>
        /// <param name="newName">The new name.</param>
        public static void RenameFile(string oldPath, string newName)
        {
            CheckFile(oldPath);

            string newFileName = Path.Combine(Path.GetDirectoryName(oldPath), newName);

            File.Move(oldPath, newFileName);
        }

        /// <summary>
        /// Renames the directory.
        /// </summary>
        /// <param name="oldPath">The old path.</param>
        /// <param name="newName">The new name.</param>
        public static void RenameDirectory(string oldPath, string newName)
        {
            CheckDirectory(oldPath);

            string newDirectoryPath = Path.Combine(Directory.GetParent(oldPath).FullName, newName);

            Directory.Move(oldPath, newDirectoryPath);
        }

        private const char Separator = '\\';

        /// <summary>
        /// Gets the parent folder for the specified path.
        /// </summary>
        /// <param name="path">The specified file path.</param>
        /// <returns>The parent folder for the specified path.</returns>
        public static string GetFilePathWithFolderName(string path)
        {
            FileInfo fi = new FileInfo(path);
            string p = Directory.GetParent(path).Name + "/" + fi.Name;
            return p;
        }

        public static string GetLineEndingCharacter(LineEndingStyle lineEndingStyle)
        {
            string terminator = string.Empty;

            switch (lineEndingStyle)
            {
                case LineEndingStyle.Windows:
                    terminator = "\r\n";
                    break;
                case LineEndingStyle.Mac:
                    terminator = "\r";
                    break;
                case LineEndingStyle.Unix:
                    terminator = "\n";
                    break;
            }

            return terminator;
        }

        /// <summary>
        /// Searches a given directory for a specific file.
        /// </summary>
        /// <param name="directory">>The directory to search in.</param>
        /// <param name="fileToSearchFor">The file to search for.</param>
        public static void LookForFile(string directory, string fileToSearchFor)
        {
            if (!Validator.ValidString(fileToSearchFor)) return;

            try
            {
                foreach (string dir in Directory.GetDirectories(directory))
                {
                    // We don't want to search the subversion directories.
                    if (!dir.Contains(Resources.Svn) && !dir.Contains(Resources.Vendor))
                    {
                        foreach (string file in Directory.GetFiles(dir, fileToSearchFor + "*", SearchOption.TopDirectoryOnly))
                        {
                            OnFileFound(new ProjectFileEventArgs(file));
                        }
                        LookForFile(dir, fileToSearchFor);
                    }
                }
            }
            catch (ArgumentException)
            {
                throw;
            }
        }

        /// <summary>
        /// Replaces the "SheBang" path specific to Unix in
        /// the dispatch.rb file.
        /// </summary>
        /// <param name="projectPath">Path to the project.</param>
        public static void PrepareForUnix(string projectPath)
        {
            ReplaceSheBang(Resources.UnixSheBang, projectPath);
        }

        /// <summary>
        /// Replaces the "SheBang" path specific to Windows in
        /// the dispatch.rb file.
        /// </summary>
        /// <param name="projectPath">Path to the project.</param>
        public static void PrepareForWindows(string projectPath)
        {
            ReplaceSheBang(Resources.WindowsSheBang, projectPath);
        }

        /// <summary>
        /// Replaces the "SheBang" path for the given project.
        /// </summary>
        /// <param name="replaceWith"></param>
        /// <param name="projectPath"></param>
        private static void ReplaceSheBang(string replaceWith, string projectPath)
        {
            string[] dispathFiles = Resources.DispatchFiles.Split(';');

            foreach (string dispatchFile in dispathFiles)
            {
                string dispatchPath = projectPath + Resources.PathToDispatchFile + dispatchFile;
                string sheBang = File.ReadAllText(dispatchPath);
                
                if (replaceWith == Resources.WindowsSheBang)
                {
                    sheBang = Regex.Replace(sheBang, Regex.Escape(Resources.UnixSheBang), Resources.WindowsSheBang,
                                  RegexOptions.IgnoreCase);
                }
                else
                {
                    sheBang = Regex.Replace(sheBang, Regex.Escape(Resources.WindowsSheBang), Resources.UnixSheBang,
                                  RegexOptions.IgnoreCase);
                }

                SaveDocument(dispatchPath, sheBang);
            }
        }

        public static bool IsRubyFile(string path)
        {
            return Path.GetExtension(path) == Resources.RubyExtension;
        }

        public static bool IsReadOnlyFile(string path)
        {
            return ((File.GetAttributes(path) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly);
        }

        public static DocumentTypes GetDocumentType(string path)
        {
            FileInfo fileInfo = new FileInfo(path.Trim('*'));

            string parentDirectoryName = fileInfo.Directory.Name;

            if (parentDirectoryName == Resources.ModelsDirectory)
            {
                return DocumentTypes.Model;
            }
            else if ((Path.GetExtension(path) == Resources.RHTMLExtension) | (Path.GetExtension(path) == Resources.RjsExtension))
            {
                return DocumentTypes.View;
            }
            else if (parentDirectoryName == Resources.ControllersDirectory)
            {
                return DocumentTypes.Controller;
            }
            else if ((parentDirectoryName == Resources.TestDirectory) | (parentDirectoryName == Resources.UnitTestDirectory) | (parentDirectoryName == Resources.FunctionalTestDirectory))
            {
                return DocumentTypes.Tests;
            }
            else if ((parentDirectoryName == Resources.SpecDirectory) | (parentDirectoryName == Resources.ModelSpecDirectory) | (parentDirectoryName == Resources.ControllerSpecDirectory))
            {
                // TODO: should there be a new doc type for specs?
                return DocumentTypes.Tests;
            }
            else
            {
                return DocumentTypes.Other;
            }
        }

        public static bool IsFile(string path)
        {
            return File.Exists(path);
        }

        public static bool IsDirectory(string path)
        {
            return Directory.Exists(path);
        }
        
        public static TestableDirectory GetTestableDirectory(string path)
        {
            DirectoryInfo dir = new DirectoryInfo(path);
            if (ProjectService.ProjectPath == dir.FullName)
                return TestableDirectory.AllTests;
            if (null == dir.Parent)
                return TestableDirectory.NotTestable;
            if (ProjectService.ProjectPath == dir.Parent.FullName)
            {
                if (dir.Name == Resources.TestDirectory)
                    return TestableDirectory.AllTests;
            }
            if (null != dir.Parent.Parent && ProjectService.ProjectPath == dir.Parent.Parent.FullName)
            {
                if (dir.Parent.Name == Resources.TestDirectory)
                    if (dir.Name == Resources.UnitTestDirectory)
                        return TestableDirectory.UnitTests;
                    else if (dir.Name == Resources.FunctionalTestDirectory)
                        return TestableDirectory.FunctionalTests;
                    else if (dir.Name == Resources.IntegrationTestDirectory)
                        return TestableDirectory.IntegrationTests;
            }
            return TestableDirectory.NotTestable;
        }
    }
}
